﻿using System;
using System.IO;
using System.Net;
using System.Security.Authentication;
using System.Xml;

namespace LiveNet.Photos
{
    public sealed partial class LivePhotos
    {
        private delegate void ResponseCompletedDelegate(Stream stream);
        private delegate void PutResponseCompletedDelegate();
        private delegate void PropFindResponseCompletedDelegate(Stream stream, bool allListed);
        private delegate void PropNodeFoundDelegate(XmlNode node, XmlNamespaceManager xmlnsm);

        /// <summary>
        /// Makes a PROPFIND request to the server, calling the <paramref name="response"/> delegate if 
        /// the HttpResponse from the server is successfull.
        /// </summary>
        /// <param name="url"></param>
        /// <param name="listAll"></param>
        /// <param name="response"></param>
        /// <exception cref="AuthenticationException"></exception>
        /// <exception cref="LiveNetRequestException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        private void MakePropFindRequest(Uri url, bool listAll, PropFindResponseCompletedDelegate response)
        {
            HttpWebRequest request = BuildRequest(url, "PROPFIND");
            request.Headers.Add("Depth", listAll ? "1" : "0");

            try
            {
                using (HttpWebResponse httpResponse = (HttpWebResponse) request.GetResponse())
                {
                    if (httpResponse.StatusCode == (HttpStatusCode) 207)
                    {
                        using (Stream stream = httpResponse.GetResponseStream())
                        {
                            response(stream, listAll);
                        }
                    }
                }
            }
            catch (WebException ex)
            {
                switch (((HttpWebResponse)ex.Response).StatusCode)
                {
                    case HttpStatusCode.BadRequest:
                        throw new LiveNetRequestException(PropFindResponseExceptionResource.BadRequest);
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(PropFindResponseExceptionResource.Unauthorized);
                    case HttpStatusCode.NotFound:
                        throw new FileNotFoundException(PropFindResponseExceptionResource.NotFound);
                    case HttpStatusCode.InternalServerError:
                        throw new LiveNetRequestException(PropFindResponseExceptionResource.InternalServerError, ex);
                    default:
                        throw new LiveNetRequestException(PropFindResponseExceptionResource.Default, ex);
                }
            }
        }

        /// <summary>
        /// Makes a GET request to the server, calling the <paramref name="response"/> delegate if 
        /// the HttpResponse from the server is successfull.
        /// </summary>
        /// <param name="url"></param>
        /// <param name="response"></param>
        /// <exception cref="LiveNetRequestException"></exception>
        /// <exception cref="AuthenticationException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        private void MakeGetRequest(Uri url, ResponseCompletedDelegate response)
        {
            HttpWebRequest request = BuildRequest(url, "GET");
            
            try
            {
                using (HttpWebResponse httpResponse = (HttpWebResponse) request.GetResponse())
                {
                    if (httpResponse.StatusCode == HttpStatusCode.OK)
                    {
                        using (Stream stream = httpResponse.GetResponseStream())
                        {
                            response(stream);
                        }
                    }
                }
            }
            catch (WebException ex)
            {
                switch (((HttpWebResponse)ex.Response).StatusCode)
                {
                    case HttpStatusCode.NoContent:
                        throw new LiveNetRequestException(GetResponseExceptionResource.NoContent, ex);
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(GetResponseExceptionResource.Unauthorized, ex);
                    case HttpStatusCode.Forbidden:
                        throw new DirectoryNotFoundException(GetResponseExceptionResource.Forbidden, ex);
                    case HttpStatusCode.NotFound:
                        throw new FileNotFoundException(GetResponseExceptionResource.NotFound, ex);
                    case HttpStatusCode.InternalServerError:
                        throw new LiveNetRequestException(GetResponseExceptionResource.InternalServerError, ex);
                    default:
                        throw new LiveNetRequestException(GetResponseExceptionResource.Default, ex);
                }
            }
        }

        /// <summary>
        /// Uses the MKCOL command to create a new folder.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="AuthenticationException"></exception>
        /// <exception cref="LiveNetRequestException"></exception>
        private bool MakeMkcolRequest(Uri url)
        {
            HttpWebRequest request = BuildRequest(url, "MKCOL");

            try
            {
                using (HttpWebResponse httpResponse = (HttpWebResponse) request.GetResponse())
                {
                    if (httpResponse.StatusCode == HttpStatusCode.Created)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            catch (WebException ex)
            {
                switch (((HttpWebResponse) ex.Response).StatusCode)
                {
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(MkcolResponseExceptionResource.Unauthorized, ex);
                    case HttpStatusCode.Forbidden:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.Forbidden, ex);
                    case HttpStatusCode.MethodNotAllowed:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.MethodNotAllowed, ex);
                    case HttpStatusCode.Conflict:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.Conflict, ex);
                    case HttpStatusCode.UnsupportedMediaType:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.UnsupportedMediaType, ex);
                    case HttpStatusCode.InternalServerError:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.InternalServerError, ex);
                    case HttpStatusCode.ServiceUnavailable:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.ServiceTemporarilyUnavailable, ex);
                    case (HttpStatusCode) 507:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.InsufficientStorage, ex);
                    default:
                        throw new LiveNetRequestException(MkcolResponseExceptionResource.Default, ex);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        /// <exception cref="AuthenticationException"></exception>
        /// <exception cref="LiveNetRequestException"></exception>
        private bool MakeDeleteRequest(Uri url)
        {
            HttpWebRequest request = BuildRequest(url, "DELETE");

            try
            {
                using (HttpWebResponse httpResponse = (HttpWebResponse) request.GetResponse())
                {
                    if (httpResponse.StatusCode == HttpStatusCode.NoContent)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            catch (WebException ex)
            {
                switch (((HttpWebResponse)ex.Response).StatusCode)
                {
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(DeleteResponseExceptionResource.Unauthorized, ex);
                    case HttpStatusCode.NotFound:
                        throw new LiveNetRequestException(DeleteResponseExceptionResource.NotFound, ex);
                    case (HttpStatusCode) 423:
                        throw new LiveNetRequestException(DeleteResponseExceptionResource.Locked, ex);
                    case HttpStatusCode.InternalServerError:
                        throw new LiveNetRequestException(DeleteResponseExceptionResource.InternalServerError, ex);
                    default:
                        throw new LiveNetRequestException(DeleteResponseExceptionResource.Default, ex);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <param name="stream"></param>
        /// <param name="responseCompleted"></param>
        /// <exception cref="AuthenticationException"></exception>
        /// <exception cref="LiveNetRequestException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        private void MakePutRequest(Uri url, Stream stream, PutResponseCompletedDelegate responseCompleted)
        {
            HttpWebRequest request = BuildRequest(url, "PUT");
            string extn = url.ToString().ToLower();
            if (extn.EndsWith("jpg") || extn.EndsWith("jpeg"))
            {
                request.ContentType = "image/jpeg";
            }
            else if (extn.EndsWith("tiff") || extn.EndsWith("tif"))
            {
                request.ContentType = "image/tiff";
            }
            else if (extn.EndsWith("gif"))
            {
                request.ContentType = "image/gif";
            }
            else 
            {
                throw new LiveNetRequestException("File type not supported");
            }

            // Add the photo stream to the WebRequest stream
            using (Stream requestStream = request.GetRequestStream())
            {
                CopyStream(stream, requestStream);
            }

            try
            {
                using (HttpWebResponse httpResponse = (HttpWebResponse) request.GetResponse())
                {
                    if (httpResponse.StatusCode == HttpStatusCode.Created ||
                        httpResponse.StatusCode == HttpStatusCode.NoContent)
                    {
                        responseCompleted();
                    }
                }
            }
            catch (WebException ex)
            {
                switch (((HttpWebResponse)ex.Response).StatusCode)
                {
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(PutResponseExceptionResource.Unauthorized, ex);
                    case HttpStatusCode.Forbidden:
                        throw new AuthenticationException(PutResponseExceptionResource.Forbidden, ex);
                    case HttpStatusCode.MethodNotAllowed:
                        throw new LiveNetRequestException(PutResponseExceptionResource.MethodNotAllowed, ex);
                    case HttpStatusCode.Conflict:
                        throw new DirectoryNotFoundException(PutResponseExceptionResource.Conflict, ex);
                    case HttpStatusCode.PreconditionFailed:
                        throw new LiveNetRequestException(PutResponseExceptionResource.PreconditionFailed, ex);
                    case HttpStatusCode.InternalServerError:
                        throw new LiveNetRequestException(PutResponseExceptionResource.InternalServerError, ex);
                    case HttpStatusCode.ServiceUnavailable:
                        throw new LiveNetRequestException(PutResponseExceptionResource.ServiceTemporarilyUnavailable, ex);
                    case (HttpStatusCode) 507:
                        throw new LiveNetRequestException(PutResponseExceptionResource.InsufficientStorage, ex);
                    default:
                        throw new LiveNetRequestException(PutResponseExceptionResource.Default, ex);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <param name="caption"></param>
        /// <param name="displayName"></param>
        /// <returns></returns>
        /// <exception cref="LiveNetRequestException"></exception>
        /// <exception cref="AuthenticationException"></exception>
        private bool MakePropPatchRequest(Uri url, string caption, string displayName)
        {
            string updateXml =
                string.Format(
                    "<D:propertyupdate xmlns:D=\"DAV:\" xmlns:c=\"http://storage.msn.com/DAV/\"><D:set><D:prop><c:DisplayName>{0}</c:DisplayName><c:Caption>{1}</c:Caption></D:prop></D:set></D:propertyupdate>",
                    displayName, caption);
            HttpWebRequest request = BuildRequest(url, "PROPPATCH");
            request.ContentType = "text/xml";
            request.ContentLength = updateXml.Length;

            using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
            {
                writer.Write(updateXml);
            }

            try
            {
                using (HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse())
                {
                    if (httpResponse.StatusCode == (HttpStatusCode) 207)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            catch (WebException ex)
            {
                switch (((HttpWebResponse)ex.Response).StatusCode)
                {
                    case HttpStatusCode.BadRequest:
                        throw new LiveNetRequestException(PropPatchResponseExceptionResource.BadRequest, ex);
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(PropPatchResponseExceptionResource.Unauthorized, ex);
                    case HttpStatusCode.Forbidden:
                        throw new AuthenticationException(PropPatchResponseExceptionResource.Forbidden, ex);
                    case HttpStatusCode.InternalServerError:
                        throw new LiveNetRequestException(PropPatchResponseExceptionResource.InternalServerError, ex);
                    case HttpStatusCode.ServiceUnavailable:
                        throw new LiveNetRequestException(PropPatchResponseExceptionResource.ServiceTemporarilyUnavailable, ex);
                    default:
                        throw new LiveNetRequestException(PropPatchResponseExceptionResource.Default, ex);
                }
            }
        }
    }
}